package org.pombo.sqm;

import org.pombo.gtm.ChanceNode;
import org.pombo.gtm.Node;
import org.pombo.gtm.PayoffNode;
import org.pombo.gtm.Player;
import org.pombo.gtm.StrategyNode;

abstract class NodeCalculatorFactory<PayoffNodeCalculator extends NodeCalculator<PayoffNode>, ChanceNodeCalculator extends NodeCalculator<ChanceNode>, PlayerNodeCalculator extends NodeCalculator<StrategyNode>, OpponentNodeCalculator extends NodeCalculator<StrategyNode>, GenericNodeCalculator extends NodeCalculator<?>> {

	private Player player;
	private PayoffNodeCalculator payoffNodeCalculator;
	private ChanceNodeCalculator chanceNodeCalculator;
	private PlayerNodeCalculator playerNodeCalculator;
	private OpponentNodeCalculator opponentNodeCalculator;

	NodeCalculatorFactory(Player player) {
		this.player = player;
	}

	void setPayoffNodeCalculator(PayoffNodeCalculator payoffNodeCalculator) {
		this.payoffNodeCalculator = payoffNodeCalculator;
	}

	void setChanceNodeCalculator(ChanceNodeCalculator chanceNodeCalculator) {
		this.chanceNodeCalculator = chanceNodeCalculator;
	}

	void setPlayerNodeCalculator(PlayerNodeCalculator playerNodeCalculator) {
		this.playerNodeCalculator = playerNodeCalculator;
	}

	void setOpponentNodeCalculator(OpponentNodeCalculator opponentNodecalculator) {
		this.opponentNodeCalculator = opponentNodecalculator;
	}

	@SuppressWarnings("unchecked")
	GenericNodeCalculator on(Node node) {
		if (this.isPayoffNode(node)) {
			this.payoffNodeCalculator.setNode((PayoffNode) node);
			return (GenericNodeCalculator) this.payoffNodeCalculator;
		} else if (this.isChanceNode(node)) {
			this.chanceNodeCalculator.setNode((ChanceNode) node);
			return (GenericNodeCalculator) this.chanceNodeCalculator;
		} else if (this.isPlayerNode(node)) {
			this.playerNodeCalculator.setNode((StrategyNode) node);
			return (GenericNodeCalculator) this.playerNodeCalculator;
		} else if (this.isOpponentNode(node)) {
			this.opponentNodeCalculator.setNode((StrategyNode) node);
			return (GenericNodeCalculator) this.opponentNodeCalculator;
		} else {
			throw new RuntimeException("Unexpected Node");
		}
	}

	private boolean isPayoffNode(Node node) {
		return node instanceof PayoffNode;
	}

	private boolean isChanceNode(Node node) {
		return node instanceof ChanceNode;
	}

	private boolean isPlayerNode(Node node) {
		return node instanceof StrategyNode
				&& ((StrategyNode) node).getPlayer().equals(this.player);
	}

	private boolean isOpponentNode(Node node) {
		return node instanceof StrategyNode
				&& !((StrategyNode) node).getPlayer().equals(this.player);
	}

}
